import zodiac from 'zodiac-ts';
// Returns the ISO week of the date.
// eslint-disable-next-line no-extend-native
Date.prototype.getWeek = function() {
  const date = new Date(this.getTime());
  date.setHours(0, 0, 0, 0);
  // Thursday in current week decides the year.
  date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
  // January 4 is always in week 1.
  const week1 = new Date(date.getFullYear(), 0, 4);
  // Adjust to Thursday in week 1 and count number of weeks from date to week1.
  const result = 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 -
      3 + (week1.getDay() + 6) % 7) / 7);
  if (result<=9) {
    return '0'+result;
  }
  return result;
};

// Returns the four-digit year corresponding to the ISO week of the date.
// eslint-disable-next-line no-extend-native
Date.prototype.getWeekYear = function() {
  const date = new Date(this.getTime());
  date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
  return date.getFullYear();
};

function getWeekIdx(d) {
  let date=new Date();
  if (d) {
    date=new Date(d);
  }
  return date.getWeekYear()+'/'+date.getWeek();
}
function getMonthIdx(d) {
  let date=new Date();
  if (d) {
    date=new Date(d);
  }
  return date.toISOString().slice(0, 7);
}
function getDayIdx(d) {
  let date=new Date();
  if (d) {
    date=new Date(d);
  }
  return date.toISOString().slice(0, 10);
}

export class CommentsModel {
  constructor(data) {
    this.data=data;
    this.length=data.length;
    this.dayMap={};
    this.weekMap={};
    this.monthMap={};
    this.earliestTime=Infinity;
    this.latestTime=0;
    this.extendedDayArray=[];
    this.extendedMonthArray=[];
    this.extendedWeekArray=[];
    let sum=0;
    let goodCount=0;
    let badCount=0;
    let mediumCount=0;
    for (const comment of this.data) {
      if (comment.content.length>10) {
        comment.content=comment.content.slice(0, 10)+'...';
      }
      sum+=comment.score;
      if (comment.score>=4) {
        goodCount++;
      } else if (comment.score>=3) {
        mediumCount++;
      } else {
        badCount++;
      }
      const date=new Date(comment.time);
      const day=getDayIdx(date);
      const month=getMonthIdx(date);
      const week=getWeekIdx(date);
      this.earliestTime=Math.min(Number(date), (this.earliestTime));
      this.latestTime=Math.max(Number(date), (this.latestTime));
      if (!this.dayMap[day]) {
        this.dayMap[day]=[];
      }
      this.dayMap[day].push(comment);
      if (!this.monthMap[month]) {
        this.monthMap[month]=[];
      }
      this.monthMap[month].push(comment);
      if (!this.weekMap[week]) {
        this.weekMap[week]=[];
      }
      this.weekMap[week].push(comment);
    }
    this.earliestTime=new Date(this.earliestTime);
    this.latestTime=new Date(this.latestTime);
    this.overall={
      goodCount, badCount, mediumCount,
      avgScore: sum/this.data.length,
    };
    const latestDay=getDayIdx(this.latestTime);
    const extendedLatestTime=new Date(this.latestTime);
    extendedLatestTime.setFullYear(extendedLatestTime.getFullYear()+1);
    const extendedLatestDay=getDayIdx(extendedLatestTime);
    // console.log({latestDay});
    for (let i=this.earliestTime; i.setDate(i.getDate() + 1);) {
      const day=getDayIdx(i);
      const month=getMonthIdx(i);
      const week=getWeekIdx(i);
      if (day<=latestDay) {
        this.dayMap[day]=this.dayMap[day] || [];
        this.monthMap[month]=this.monthMap[month] || [];
        this.weekMap[week]=this.weekMap[week] || [];
      }
      if (this.extendedDayArray.at(-1)!==day) this.extendedDayArray.push(day);
      if (this.extendedWeekArray.at(-1)!==week) this.extendedWeekArray.push(week);
      if (this.extendedMonthArray.at(-1)!==month) this.extendedMonthArray.push(month);
      if (day>extendedLatestDay) {
        break;
      }
    }

    this.dayArray=this.convertToArray(this.dayMap);
    this.weekArray=this.convertToArray(this.weekMap);
    this.monthArray=this.convertToArray(this.monthMap);

    this.monthAvg=this.getAvg(this.monthMap);

    if (this.monthArray.at(-1).key===getMonthIdx()) {
      this.monthArray.pop();
    }
    if (this.dayArray.at(-1).key===getDayIdx()) {
      this.dayArray.pop();
    }
    if (this.weekArray.at(-1).key===getWeekIdx()) {
      this.weekArray.pop();
    }

    this.monthPredict = this.combineXY(this.extendedMonthArray, this.predict(this.monthArray)).slice(this.monthArray.length);
    // console.log(this.monthPredict);
  }
  convertToArray(obj) {
    return Object.entries(obj).map(
        ([key, value])=>({key, value: value.length, type: '评论量'}),
    ).sort(({key: keyA}, {key: keyB})=>keyA>keyB?1:-1);
  }
  predict(arr) {
    const data = arr.map(({value})=>value);
    const seasonLength = 12;
    const multiplicative = false; // indicates if the model is additive or multiplicative

    const hws = new zodiac.HoltWintersSmoothing(data, 0, 0, 0, seasonLength, multiplicative);
    hws.optimizeParameters(20); // return an object containing the optimized values of alpha, gamma and delta
    return hws.predict();
  }
  combineXY(key, value) {
    return key.map((k, index)=>({
      key: k, value: (typeof value[index] === 'number' && !isNaN(value[index]))?Math.max(0, value[index]).toFixed(2):null, type: '预测',
    }));
  }
  getAvg(map) {
    const result=[];
    const entries=Object.entries(map);
    entries.sort(([key1], [key2])=>key1<key2?-1:1);
    for (const [key, arr] of entries) {
      let sum=0;
      for (const {score} of arr) {
        sum+=score;
      }
      sum/=arr.length;
      result.push({
        avg: sum?Number(sum.toFixed(2)):result.at(-1).avg, key,
      });
    }
    return result;
  }
}
